﻿using Microsoft.VisualStudio.Editor;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.TextManager.Interop;
using System;
using System.ComponentModel.Design;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Task = System.Threading.Tasks.Task;

namespace ProtoConvertor
{
    /// <summary>
    /// Command handler
    /// </summary>
    internal sealed class CodeFormatCommand
    {
        public class InputBox : Form
        {
            private TextBox textBox;
            private Button okButton;
            private Button cancelButton;

            public string Caption
            {
                get { return Text; }
                set { Text = value; }
            }

            public string Prompt
            {
                get { return textBox.Text; }
                set { textBox.Text = value; }
            }

            public string Value
            {
                get { return textBox.Text; }
                set { textBox.Text = value; }
            }

            public InputBox()
            {
                // 初始化组件
                textBox = new TextBox { Left = 50, Top = 20, Width = 200 };
                okButton = new Button { Text = "OK", Left = 150, Width = 100, Top = 70, DialogResult = DialogResult.OK };
                cancelButton = new Button { Text = "Cancel", Left = 250, Width = 100, Top = 70, DialogResult = DialogResult.Cancel };
                Text = Caption;

                // 添加组件
                Controls.Add(textBox);
                Controls.Add(okButton);
                Controls.Add(cancelButton);

                // 设置对话框属性
                AcceptButton = okButton;
                CancelButton = cancelButton;
                StartPosition = FormStartPosition.CenterScreen;
                FormBorderStyle = FormBorderStyle.FixedDialog;
                MaximizeBox = false;
                MinimizeBox = false;
                ClientSize = new Size(400, 150);
            }
        }
        /// <summary>
        /// Command ID.
        /// </summary>
        public const int CommandId = 0x0100;

        /// <summary>
        /// Command menu group (command set GUID).
        /// </summary>
        public static readonly Guid CommandSet = new Guid("71272c0b-5cb8-439b-ad7e-32c0226830fa");

        /// <summary>
        /// VS Package that provides this command, not null.
        /// </summary>
        private readonly AsyncPackage package;

        /// <summary>
        /// Initializes a new instance of the <see cref="CodeFormatCommand"/> class.
        /// Adds our command handlers for menu (commands must exist in the command table file)
        /// </summary>
        /// <param name="package">Owner package, not null.</param>
        /// <param name="commandService">Command service to add command to, not null.</param>
        private CodeFormatCommand(AsyncPackage package, OleMenuCommandService commandService)
        {
            this.package = package ?? throw new ArgumentNullException(nameof(package));
            commandService = commandService ?? throw new ArgumentNullException(nameof(commandService));

            var menuCommandID = new CommandID(CommandSet, CommandId);
            var menuItem = new MenuCommand(this.Execute, menuCommandID);
            commandService.AddCommand(menuItem);
        }

        /// <summary>
        /// Gets the instance of the command.
        /// </summary>
        public static CodeFormatCommand Instance
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the service provider from the owner package.
        /// </summary>
        private Microsoft.VisualStudio.Shell.IAsyncServiceProvider ServiceProvider
        {
            get
            {
                return this.package;
            }
        }

        /// <summary>
        /// Initializes the singleton instance of the command.
        /// </summary>
        /// <param name="package">Owner package, not null.</param>
        public static async Task InitializeAsync(AsyncPackage package)
        {
            // Switch to the main thread - the call to AddCommand in CodeFormatCommand's constructor requires
            // the UI thread.
            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken);

            OleMenuCommandService commandService = await package.GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService;
            Instance = new CodeFormatCommand(package, commandService);
        }

        private string GetUserInput()
        {
            // 这个函数需要你创建一个对话框，然后返回用户输入的字符串
            // 如果用户点击了取消按钮，返回null
            InputBox inputBox = new InputBox
            {
                Caption = "Enter variable name",
                Prompt = "Variable name:",
            };

            // 显示输入对话框并获取用户输入
            if (inputBox.ShowDialog() == DialogResult.OK)
            {
                return inputBox.Value;
            }
            else
            {
                // 用户点击了取消按钮
                return null;
            }
        }

        private string TransformMemberName(string member, bool isReadAccess, string value = null)
        {
            if (!member.Any(char.IsUpper))
            {
                //// Check if read access is already correct
                //先屏蔽，无法处理info.id这类情况
                //if (isReadAccess)
                //{
                //    return member;
                //}
                // Check if write access is already correct
                /*else */if (!isReadAccess && member.StartsWith("set_"))
                {
                    return member;
                }
            }

            // Convert camelCase (excluding type prefix) to lower_snake_case
            var sb = new StringBuilder();
            var prefix = new StringBuilder();
            bool prefixFinished = false;
            bool bFirst = true;
            for (int i = 0; i < member.Length; ++i)
            {
                if (char.IsUpper(member[i]))
                {
                    prefixFinished = true;
                }


                if (prefixFinished)
                {
                    if (char.IsUpper(member[i]) && i != 0)
                    {
                        if (!bFirst)
                        {
                            sb.Append('_');
                        }
                        bFirst = false;
                    }

                    sb.Append(char.ToLowerInvariant(member[i]));
                }
                else
                {
                    prefix.Append(member[i]);
                }
            }

            string snakeCaseName = sb.ToString();

            if (snakeCaseName.Length > 0)
            {
                if (prefix.ToString() == "id")
                {
                    snakeCaseName += "_id";
                }
            }
            else
            {
                snakeCaseName = prefix.ToString();
            }

            // Append '()' for read access and prepend 'set_' and append '(value)' for write access
            if (isReadAccess)
            {
                return $"{snakeCaseName}()";
            }
            else
            {
                return $"set_{snakeCaseName}({value})";
            }
        }

        private string ProcessLine(string line, string varName)
        {
            var readAccessMatches = Regex.Matches(line, $@"{varName}(\.|\-\>)(\w+)");
            var writeAccessMatches = Regex.Matches(line, $@"{varName}(\.|\-\>)(\w+)\s*[^=!\>\<\s]*=\s*(.+);");

            foreach (Match match in writeAccessMatches)
            {
                string part = match.Groups[0].Value;
                string seprator = match.Groups[1].Value;
                string member = match.Groups[2].Value;
                string value = match.Groups[3].Value;
                string transformedMember = TransformMemberName(member, false, value);
                line = line.Replace(part, $"{varName}{seprator}{transformedMember};");
            }

            foreach (Match match in readAccessMatches)
            {
                string part = match.Groups[0].Value;
                string seprator = match.Groups[1].Value;
                string member = match.Groups[2].Value;
                string transformedMember = TransformMemberName(member, true);
                line = line.Replace(part, $"{varName}{seprator}{transformedMember}");
            }

            return line;
        }


        /// <summary>
        /// This function is the callback used to execute the command when the menu item is clicked.
        /// See the constructor to see how the menu item is associated with this function using
        /// OleMenuCommandService service and MenuCommand class.
        /// </summary>
        /// <param name="sender">Event sender.</param>
        /// <param name="e">Event args.</param>
        private void Execute(object sender, EventArgs e)
        {
            string targetVariable = GetUserInput();

            if (string.IsNullOrEmpty(targetVariable))
            {
                return;
            }

            IVsTextManager txtMgr = (IVsTextManager)Package.GetGlobalService(typeof(SVsTextManager));
            IVsTextView vTextView = null;
            int mustHaveFocus = 1;
            txtMgr.GetActiveView(mustHaveFocus, null, out vTextView);
            IVsUserData userData = vTextView as IVsUserData;

            if (userData == null)
            {
                Console.WriteLine("No text view is currently open");
                return;
            }

            IWpfTextViewHost viewHost;
            object holder;
            Guid guidViewHost = DefGuidList.guidIWpfTextViewHost;
            userData.GetData(ref guidViewHost, out holder);
            viewHost = (IWpfTextViewHost)holder;

            ITextSelection selection = viewHost.TextView.Selection;

            if (!selection.IsEmpty)
            {
                ITextDocument document;
                viewHost.TextView.TextDataModel.DocumentBuffer.Properties.TryGetProperty(typeof(ITextDocument), out document);

                ITextSnapshot snapshot = viewHost.TextView.TextBuffer.CurrentSnapshot;

                // Process the selected lines
                ITextEdit edit = viewHost.TextView.TextBuffer.CreateEdit();
                foreach (var span in selection.SelectedSpans)
                {
                    var startLine = snapshot.GetLineFromPosition(span.Start.Position);
                    var endLine = snapshot.GetLineFromPosition(span.End.Position);

                    for (int lineNumber = startLine.LineNumber; lineNumber <= endLine.LineNumber; lineNumber++)
                    {
                        var line = snapshot.GetLineFromLineNumber(lineNumber);
                        string originalLine = line.GetText();
                        string modifiedLine = ProcessLine(originalLine, targetVariable);
                        if (originalLine != modifiedLine)
                        {
                            edit.Replace(line.Start, line.Length, modifiedLine);
                        }
                    }
                }

                // Apply the modifications to the edit
                if (edit.HasEffectiveChanges)
                {
                    edit.Apply();
                }
            }
        }


    }
}
